/*
 * contrib/btree_gist/btree_numeric.c
 */
#include "postgres.h"

#include <math.h>
#include <float.h>

#include "btree_gist.h"
#include "btree_utils_var.h"
#include "utils/builtins.h"
#include "utils/numeric.h"
#include "utils/rel.h"

/*
** Bytea 操作
*/
PG_FUNCTION_INFO_V1(gbt_numeric_compress);
PG_FUNCTION_INFO_V1(gbt_numeric_union);
PG_FUNCTION_INFO_V1(gbt_numeric_picksplit);
PG_FUNCTION_INFO_V1(gbt_numeric_consistent);
PG_FUNCTION_INFO_V1(gbt_numeric_penalty);
PG_FUNCTION_INFO_V1(gbt_numeric_same);


/* 定义比较 */

static bool fc_gbt_numeric_gt(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(numeric_gt,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_numeric_ge(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(numeric_ge,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_numeric_eq(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(numeric_eq,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_numeric_le(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(numeric_le,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static bool fc_gbt_numeric_lt(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetBool(DirectFunctionCall2(numeric_lt,
											PointerGetDatum(fc_a),
											PointerGetDatum(fc_b)));
}

static int32 fc_gbt_numeric_cmp(const void *fc_a, const void *fc_b, Oid fc_collation, FmgrInfo *fc_flinfo)
{
	return DatumGetInt32(DirectFunctionCall2(numeric_cmp,
											 PointerGetDatum(fc_a),
											 PointerGetDatum(fc_b)));
}


static const gbtree_vinfo tinfo =
{
	gbt_t_numeric,
	0,
	false,
	fc_gbt_numeric_gt,
	fc_gbt_numeric_ge,
	fc_gbt_numeric_eq,
	fc_gbt_numeric_le,
	fc_gbt_numeric_lt,
	fc_gbt_numeric_cmp,
	NULL
};


/**************************************************
 * 文本操作
 **************************************************/


Datum gbt_numeric_compress(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);

	PG_RETURN_POINTER(gbt_var_compress(fc_entry, &tinfo));
}



Datum gbt_numeric_consistent(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_entry = (GISTENTRY *) PG_GETARG_POINTER(0);
	void	   *fc_query = (void *) DatumGetNumeric(PG_GETARG_DATUM(1));
	StrategyNumber fc_strategy = (StrategyNumber) PG_GETARG_UINT16(2);

	/* Oid		subtype = PG_GETARG_OID(3); */
	bool	   *fc_recheck = (bool *) PG_GETARG_POINTER(4);
	bool		fc_retval;
	GBT_VARKEY *fc_key = (GBT_VARKEY *) DatumGetPointer(fc_entry->key);
	GBT_VARKEY_R fc_r = gbt_var_key_readable(fc_key);

	/* 由此函数服务的所有案例都是精确的 */
	*fc_recheck = false;

	fc_retval = gbt_var_consistent(&fc_r, fc_query, fc_strategy, PG_GET_COLLATION(),
								GIST_LEAF(fc_entry), &tinfo, fcinfo->flinfo);
	PG_RETURN_BOOL(fc_retval);
}



Datum gbt_numeric_union(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	int32	   *fc_size = (int *) PG_GETARG_POINTER(1);

	PG_RETURN_POINTER(gbt_var_union(fc_entryvec, fc_size, PG_GET_COLLATION(),
									&tinfo, fcinfo->flinfo));
}


Datum gbt_numeric_same(PG_FUNCTION_ARGS)
{
	Datum		fc_d1 = PG_GETARG_DATUM(0);
	Datum		fc_d2 = PG_GETARG_DATUM(1);
	bool	   *fc_result = (bool *) PG_GETARG_POINTER(2);

	*fc_result = gbt_var_same(fc_d1, fc_d2, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo);
	PG_RETURN_POINTER(fc_result);
}


Datum gbt_numeric_penalty(PG_FUNCTION_ARGS)
{
	GISTENTRY  *fc_o = (GISTENTRY *) PG_GETARG_POINTER(0);
	GISTENTRY  *fc_n = (GISTENTRY *) PG_GETARG_POINTER(1);
	float	   *fc_result = (float *) PG_GETARG_POINTER(2);

	Numeric		fc_us,
				fc_os,
				fc_ds;

	GBT_VARKEY *fc_org = (GBT_VARKEY *) DatumGetPointer(fc_o->key);
	GBT_VARKEY *fc_newe = (GBT_VARKEY *) DatumGetPointer(fc_n->key);
	Datum		fc_uni;
	GBT_VARKEY_R fc_rk,
				fc_ok,
				fc_uk;

	fc_rk = gbt_var_key_readable(fc_org);
	fc_uni = PointerGetDatum(gbt_var_key_copy(&fc_rk));
	gbt_var_bin_union(&fc_uni, fc_newe, PG_GET_COLLATION(), &tinfo, fcinfo->flinfo);
	fc_ok = gbt_var_key_readable(fc_org);
	fc_uk = gbt_var_key_readable((GBT_VARKEY *) DatumGetPointer(fc_uni));

	fc_us = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
											 PointerGetDatum(fc_uk.upper),
											 PointerGetDatum(fc_uk.lower)));

	fc_os = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
											 PointerGetDatum(fc_ok.upper),
											 PointerGetDatum(fc_ok.lower)));

	fc_ds = DatumGetNumeric(DirectFunctionCall2(numeric_sub,
											 NumericGetDatum(fc_us),
											 NumericGetDatum(fc_os)));

	if (numeric_is_nan(fc_us))
	{
		if (numeric_is_nan(fc_os))
			*fc_result = 0.0;
		else
			*fc_result = 1.0;
	}
	else
	{
		Numeric		fc_nul = int64_to_numeric(0);

		*fc_result = 0.0;

		if (DirectFunctionCall2(numeric_gt, NumericGetDatum(fc_ds), NumericGetDatum(fc_nul)))
		{
			*fc_result += FLT_MIN;
			fc_os = DatumGetNumeric(DirectFunctionCall2(numeric_div,
													 NumericGetDatum(fc_ds),
													 NumericGetDatum(fc_us)));
			*fc_result += (float4) DatumGetFloat8(DirectFunctionCall1(numeric_float8_no_overflow, NumericGetDatum(fc_os)));
		}
	}

	if (*fc_result > 0)
		*fc_result *= (FLT_MAX / (((GISTENTRY *) PG_GETARG_POINTER(0))->rel->rd_att->natts + 1));

	PG_RETURN_POINTER(fc_result);
}



Datum gbt_numeric_picksplit(PG_FUNCTION_ARGS)
{
	GistEntryVector *fc_entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
	GIST_SPLITVEC *fc_v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);

	gbt_var_picksplit(fc_entryvec, fc_v, PG_GET_COLLATION(),
					  &tinfo, fcinfo->flinfo);
	PG_RETURN_POINTER(fc_v);
}
